package com.wenx.v3gateway.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

/**
 * 开发环境请求响应日志过滤器
 * 仅在dev环境下打印请求体和响应体，用于调试
 * 
 * @author wenx
 */
@Slf4j
@Component
public class DevLogFilter implements GlobalFilter, Ordered {

    @Value("${spring.profiles.active:}")
    private String activeProfile;
    
    private final ObjectMapper objectMapper = new ObjectMapper();

    /**
     * 忽略日志的路径
     */
    private static final List<String> IGNORE_PATHS = Arrays.asList(
            "/actuator",
            "/health",
            "/metrics",
            "/doc.html",
            "/swagger",
            "/v3/api-docs",
            "/webjars",
            "/favicon.ico"
    );

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 只在开发环境启用
        if (!"dev".equals(activeProfile)) {
            return chain.filter(exchange);
        }

        ServerHttpRequest request = exchange.getRequest();
        String path = request.getURI().getPath();

        // 忽略特定路径
        if (shouldIgnorePath(path)) {
            return chain.filter(exchange);
        }

        // 记录请求开始时间
        long startTime = System.currentTimeMillis();
        
        // 从已有的headers中获取trace信息（GatewayTraceFilter已经处理过）
        String requestId = request.getHeaders().getFirst("X-Trace-Id");
        if (requestId == null) {
            requestId = request.getHeaders().getFirst("request-id");
        }
        if (requestId == null) {
            requestId = "unknown";
        }
        
        // 从headers中获取用户认证信息
        GatewayTraceFilter.UserAuthInfo authInfo = new GatewayTraceFilter.UserAuthInfo();
        authInfo.setUserId(request.getHeaders().getFirst("X-User-Id"));
        authInfo.setUsername(request.getHeaders().getFirst("X-Username"));
        authInfo.setTenantId(request.getHeaders().getFirst("X-Tenant-Id"));
        authInfo.setDepartmentId(request.getHeaders().getFirst("X-Dept-Id"));
        
        // 记录请求基本信息
        logRequestInfo(request, requestId, authInfo);

        // 处理请求体和响应体
        return handleRequestAndResponse(exchange, chain, requestId, startTime);
    }

    /**
     * 处理请求和响应
     */
    private Mono<Void> handleRequestAndResponse(ServerWebExchange exchange, GatewayFilterChain chain, 
                                              String requestId, long startTime) {
        ServerHttpRequest request = exchange.getRequest();
        
        // 如果是有请求体的方法，记录请求体
        if (hasRequestBody(request)) {
            return handleRequestWithBody(exchange, chain, requestId, startTime);
        } else {
            // 没有请求体，直接处理响应
            return handleResponse(exchange, chain, requestId, startTime);
        }
    }

    /**
     * 处理有请求体的请求
     */
    private Mono<Void> handleRequestWithBody(ServerWebExchange exchange, GatewayFilterChain chain, 
                                           String requestId, long startTime) {
        ServerHttpRequest request = exchange.getRequest();
        
        ServerHttpRequestDecorator requestDecorator = new ServerHttpRequestDecorator(request) {
            @Override
            public Flux<DataBuffer> getBody() {
                return super.getBody().collectList().flatMapMany(dataBuffers -> {
                    try {
                        // 计算总长度
                        int totalLength = dataBuffers.stream().mapToInt(DataBuffer::readableByteCount).sum();
                        byte[] content = new byte[totalLength];
                        int currentIndex = 0;
                        
                        // 逐个读取DataBuffer内容
                        for (DataBuffer dataBuffer : dataBuffers) {
                            int length = dataBuffer.readableByteCount();
                            dataBuffer.read(content, currentIndex, length);
                            currentIndex += length;
                        }
                        
                        // 记录请求体
                        String requestBody = new String(content, StandardCharsets.UTF_8);
                        logRequestBody(requestId, requestBody);
                        
                        // 重新创建DataBuffer供后续使用
                        DataBuffer newBuffer = exchange.getResponse().bufferFactory().wrap(content);
                        return Flux.just(newBuffer);
                        
                    } catch (Exception e) {
                        log.warn("[{}] 读取请求体失败: {}", requestId, e.getMessage());
                        return Flux.fromIterable(dataBuffers);
                    } finally {
                        // 释放原始DataBuffer
                        dataBuffers.forEach(DataBufferUtils::release);
                    }
                });
            }
        };

        ServerWebExchange decoratedExchange = exchange.mutate()
                .request(requestDecorator)
                .build();

        return handleResponse(decoratedExchange, chain, requestId, startTime);
    }

    /**
     * 处理响应
     */
    private Mono<Void> handleResponse(ServerWebExchange exchange, GatewayFilterChain chain, 
                                    String requestId, long startTime) {
        ServerHttpResponse response = exchange.getResponse();
        DataBufferFactory bufferFactory = response.bufferFactory();
        
        ServerHttpResponseDecorator responseDecorator = new ServerHttpResponseDecorator(response) {
            @Override
            public Mono<Void> writeWith(org.reactivestreams.Publisher<? extends DataBuffer> body) {
                if (body instanceof Flux) {
                    Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
                    
                    return super.writeWith(fluxBody.collectList().map(dataBuffers -> {
                        // 计算总长度
                        int totalLength = dataBuffers.stream().mapToInt(DataBuffer::readableByteCount).sum();
                        byte[] content = new byte[totalLength];
                        int currentIndex = 0;
                        
                        // 逐个读取DataBuffer内容
                        for (DataBuffer dataBuffer : dataBuffers) {
                            int length = dataBuffer.readableByteCount();
                            dataBuffer.read(content, currentIndex, length);
                            currentIndex += length;
                        }
                        
                        // 释放原始DataBuffer
                        dataBuffers.forEach(DataBufferUtils::release);
                        
                        // 记录响应体
                        String responseBody = new String(content, StandardCharsets.UTF_8);
                        
                        // 获取Content-Type
                        String contentType = response.getHeaders().getFirst(HttpHeaders.CONTENT_TYPE);
                        logResponseBody(requestId, responseBody, startTime, contentType);
                        
                        return bufferFactory.wrap(content);
                    }));
                }
                return super.writeWith(body);
            }
        };

        return chain.filter(exchange.mutate().response(responseDecorator).build());
    }

    /**
     * 记录请求基本信息
     */
    private void logRequestInfo(ServerHttpRequest request, String requestId, GatewayTraceFilter.UserAuthInfo authInfo) {
        try {
            Map<String, Object> logData = new LinkedHashMap<>();
            logData.put("type", "request_start");
            logData.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
            logData.put("requestId", requestId);
            logData.put("method", request.getMethod().toString());
            logData.put("path", request.getURI().getPath());
            logData.put("fullUrl", request.getURI().toString());
            
            // 用户认证信息
            if (authInfo != null) {
                Map<String, Object> authData = new LinkedHashMap<>();
                authData.put("userId", authInfo.getUserId());
                authData.put("username", authInfo.getUsername());
                authData.put("tenantId", authInfo.getTenantId());
                authData.put("departmentId", authInfo.getDepartmentId());
                logData.put("auth", authData);
            }
            
            // 客户端IP
            String clientIp = GatewayTraceFilter.extractClientIp(request);
            if (clientIp != null) {
                logData.put("clientIp", clientIp);
            }

            // 记录请求头（过滤敏感信息）
            HttpHeaders headers = request.getHeaders();
            if (!headers.isEmpty()) {
                Map<String, Object> headerData = new LinkedHashMap<>();
                headers.forEach((name, values) -> {
                    if (isSensitiveHeader(name)) {
                        headerData.put(name, "***");
                    } else {
                        headerData.put(name, String.join(", ", values));
                    }
                });
                logData.put("headers", headerData);
            }

            // 记录查询参数（过滤敏感信息）
            MultiValueMap<String, String> queryParams = request.getQueryParams();
            if (!queryParams.isEmpty()) {
                Map<String, Object> paramData = new LinkedHashMap<>();
                queryParams.forEach((name, values) -> {
                    if (isSensitiveParam(name)) {
                        paramData.put(name, "***");
                    } else {
                        paramData.put(name, String.join(", ", values));
                    }
                });
                logData.put("queryParams", paramData);
            }

            log.info(objectMapper.writeValueAsString(logData));
        } catch (Exception e) {
            log.warn("[{}] 记录请求信息失败: {}", requestId, e.getMessage());
        }
    }

    /**
     * 记录请求体
     */
    private void logRequestBody(String requestId, String requestBody) {
        if (requestBody != null && !requestBody.trim().isEmpty()) {
            try {
                Map<String, Object> logData = new LinkedHashMap<>();
                logData.put("type", "request_body");
                logData.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
                logData.put("requestId", requestId);
                logData.put("body", requestBody);
                logData.put("bodySize", requestBody.length());
                
                log.info(objectMapper.writeValueAsString(logData));
            } catch (Exception e) {
                log.warn("[{}] 记录请求体失败: {}", requestId, e.getMessage());
            }
        }
    }

    /**
     * 记录响应体
     */
    private void logResponseBody(String requestId, String responseBody, long startTime, String contentType) {
        try {
            long duration = System.currentTimeMillis() - startTime;
            String displayBody = formatResponseBody(responseBody, contentType);
            
            Map<String, Object> logData = new LinkedHashMap<>();
            logData.put("type", "request_end");
            logData.put("timestamp", LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")));
            logData.put("requestId", requestId);
            logData.put("duration", duration);
            logData.put("performanceLevel", getPerformanceLevel(duration));
            logData.put("contentType", contentType);
            
            if (displayBody != null && !displayBody.isEmpty()) {
                if (displayBody.startsWith("[二进制内容") || displayBody.startsWith("[空响应体]")) {
                    logData.put("bodyType", "binary_or_empty");
                    logData.put("bodyDescription", displayBody);
                } else {
                    logData.put("bodyType", "text");
                    logData.put("body", displayBody);
                    logData.put("bodySize", displayBody.length());
                }
            }
            
            log.info(objectMapper.writeValueAsString(logData));
        } catch (Exception e) {
            log.warn("[{}] 记录响应体失败: {}", requestId, e.getMessage());
        }
    }

    /**
     * 格式化响应体内容
     */
    private String formatResponseBody(String responseBody, String contentType) {
        if (responseBody == null || responseBody.isEmpty()) {
            return "[空响应体]";
        }
        
        // 简化Content-Type判断
        if (contentType != null) {
            String lowerType = contentType.toLowerCase();
            if (lowerType.contains("image/") || lowerType.contains("audio/") || 
                lowerType.contains("video/") || lowerType.contains("application/octet-stream") ||
                lowerType.contains("application/pdf")) {
                return "[二进制内容，Content-Type: " + contentType + "，长度: " + responseBody.length() + " 字节]";
            }
        }
        
        return responseBody;
    }



    /**
     * 判断是否有请求体
     */
    private boolean hasRequestBody(ServerHttpRequest request) {
        HttpMethod method = request.getMethod();
        return method == HttpMethod.POST || method == HttpMethod.PUT || method == HttpMethod.PATCH;
    }

    /**
     * 判断是否应该忽略的路径
     */
    private boolean shouldIgnorePath(String path) {
        return IGNORE_PATHS.stream().anyMatch(path::startsWith);
    }

    /**
     * 判断是否为敏感请求头
     */
    private boolean isSensitiveHeader(String headerName) {
        String lowerName = headerName.toLowerCase();
        return lowerName.contains("authorization") || 
               lowerName.contains("token") || 
               lowerName.contains("password") ||
               lowerName.contains("secret");
    }

    /**
     * 判断是否为敏感参数
     */
    private boolean isSensitiveParam(String paramName) {
        String lowerName = paramName.toLowerCase();
        return lowerName.contains("password") || 
               lowerName.contains("token") || 
               lowerName.contains("secret") ||
               lowerName.contains("key");
    }

    /**
     * 根据执行时间获取性能级别
     */
    private String getPerformanceLevel(long duration) {
        if (duration >= 5000) {
            return "critical";
        } else if (duration >= 2000) {
            return "warning";
        } else if (duration >= 1000) {
            return "slow";
        } else if (duration >= 500) {
            return "normal";
        } else {
            return "fast";
        }
    }

    @Override
    public int getOrder() {
        // 在GatewayTraceFilter之后执行，确保能够获取到统一的trace信息
        return Ordered.HIGHEST_PRECEDENCE + 1;
    }
}
